package Thread;
/**模拟实现简单的阻塞队列*/
class MyBlockingQueue {
    private String[] data = null;
    // 队首
    private int head = 0;
    // 队尾
    private int tail = 0;
    // 元素个数
    private int size = 0;
    public MyBlockingQueue(int capacity) {
        data = new String[capacity];
    }

    public void put(String elem) throws InterruptedException {
        synchronized (this) {
            if(size >= data.length) {
                // 队列满了，阻塞,当成功执行了take，被唤醒
                // return;
                this.wait();
            }
            data[tail] = elem;
            tail++;
            if(tail >= data.length) {
                tail = 0;
            }
            // tail = (tail+1)%data.length;
            size++;
            this.notify();// 唤醒take中的wait，继续干活
        }
    }

    public String take() throws InterruptedException {
        synchronized (this) {
            if(size == 0) {
                // 队列为空，阻塞,当其他线程执行成功put的时候，需要唤醒
                // return null;
                this.wait();
            }
            String res = data[head];
            head++;
            if(head >= data.length) {
                head = 0;
            }
            size--;
            this.notify(); // 队列不空，唤醒put里的wait
            return res;
        }
    }

}

public class demo31 {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue(1000);
        Thread producer = new Thread(() -> {
            int n = 0;
            while (true) {
                try {
                    queue.put(n + "");
                    System.out.println("生产元素 " + n);
                    n++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        Thread consumer = new Thread(() -> {
            while (true) {
                String n = null;
                try {
                    n = queue.take();
                    System.out.println("消费元素 " + n);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        producer.start();
        consumer.start();
    }
}

/*1.没有线程等待：如果put方法在队列未满时被调用，并且没有其他线程因为调用take
方法而处于等待状态，那么notify()不会唤醒任何线程，因为没有线程在等待。
2.有take方法中的线程等待：如果队列曾经达到过其容量上限，并且take
方法中的线程因此被阻塞，那么在某个put方法调用notify()时，它可能会唤醒一个（或多个，如果使用了notifyAll()）等待的take方法线程。
3.有多个线程等待：如果有多个线程因为调用take方法而被阻塞，notify()
将随机选择一个线程来唤醒。如果有不同类型的多个线程在等待（比如既有等待put的也有等待take的），notify()同样会随机唤醒一个。 */

/*需要注意的是，notify()不会保证立即唤醒一个线程。它只是告诉JVM，
当前线程已经完成了对共享资源的操作，可以允许其他线程继续执行。线程的实际唤醒是由JVM的线程调度器决定的。

在多生产者-消费者的场景中，通常推荐使用notifyAll()来代替notify()，
因为notifyAll()会唤醒所有等待的线程，让它们自己去竞争锁，这样可以防止可能的线程饥饿问题，即某些线程可能永远不会被notify()唤醒。 */